package com.baidu.acg.industry.bigdata.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baidu.acg.industry.bigdata.config.ApplicationConfiguration;
import com.baidu.acg.industry.bigdata.model.PdParamsModel;
import com.baidu.acg.industry.bigdata.controller.response.FilePathResponse;
import com.baidu.acg.industry.bigdata.controller.response.PathUrl;
import com.baidu.acg.industry.bigdata.controller.response.PreEpochResponse;
import com.baidu.acg.industry.bigdata.utils.DataUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.ListObjectsArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.Result;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Item;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;


@Slf4j
@NoArgsConstructor(force = true)
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SiFangModelService {
    private final ApplicationConfiguration modelConfiguration;


    /**
     * @return 返回处理进度
     */
    public PreEpochResponse getPreDataStream(String modelTaskId, String folderType, String fileType) {
        PreEpochResponse epochResponse = new PreEpochResponse();
//        String minioFolder = DataUtils.minioFolderPathSub(modelTaskId);
        MinioClient minioClient = createMinioClient();
        String epoch = "";
        String filePath = modelConfiguration.getMinioSiFangFolderPrefix() + modelTaskId + "/" + folderType + "/";
        log.info("minio路径存储path=>" + filePath);
        try {
            InputStream stream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(modelConfiguration.getBucketName())
                            .object(modelConfiguration.getMinioSiFangFolderPrefix()
                                    + modelTaskId + "/" + folderType + "/" + fileType + ".json") // todo 这里的文件路径以实际的部署路径为准
                            .build()
            );
            String content = new BufferedReader(new InputStreamReader(stream))
                    .lines().collect(Collectors.joining("\n"));
            epoch = parseLastEpoch(content);
            epochResponse.setEpoch(epoch);
        } catch (ErrorResponseException | InsufficientDataException | InternalException |
                 InvalidKeyException | InvalidResponseException | IOException |
                 NoSuchAlgorithmException | ServerException | XmlParserException e) {
            log.error("操作失败: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return epochResponse;
    }

    public static String parseLastEpoch(String json) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        // 解析 JSON 到树形结构
        JsonNode rootNode = mapper.readTree(json);
        // 验证数据结构
        if (!rootNode.isArray()) {
            throw new IllegalArgumentException("Invalid JSON format: expected array");
        }
        // 转换为可遍历的数组
        List<JsonNode> nodes = rootNode.findParents("epoch");
        // 获取最后一条数据
        if (nodes.isEmpty()) {
            return null;
        }
        JsonNode lastNode = nodes.get(nodes.size() - 1);
        return lastNode.get("epoch").asText();
    }


    /**
     * TODO 这里需要区分查询模型数据文件和vtp文件，需要接口那里传过来一个type区分，然后再去拼接路径，去minio查询
     *
     * @param taskModelId
     * @return
     */
    public FilePathResponse getModelFileList(String taskModelId, String fileType) {
        FilePathResponse filePathResponse = new FilePathResponse();
//        String folderPath = minioFolderPathSub(taskModelId);
        // 这里拿到目录之后需要拼接一下下一次目录
        // folderPath需要看模型输出后是否会在folderPath下面新建文件夹
        assert modelConfiguration != null;
        String path = modelConfiguration.getMinioSiFangTrainJson() + taskModelId + "/" + fileType + "/";
        // 初始化 MinIO 客户端
        MinioClient minioClient = createMinioClient();
        List<PathUrl> filePath = new ArrayList<>();

        try {
            // 检查存储桶是否存在
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(modelConfiguration.getBucketName())
                    .build());
            if (!bucketExists) {
                log.error("Bucket '" + modelConfiguration.getBucketName() + "' 不存在！");
                return new FilePathResponse();
            }

            // 列出文件夹下的所有文件（包括子目录）
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(modelConfiguration.getBucketName())
                            .prefix(path)          // 指定文件夹前缀
                            .recursive(false)            // 不递归子目录
                            .build()
            );
            // 遍历结果并生成访问路径
            for (Result<Item> result : results) {
                Item item = result.get();
                if (!item.isDir()) {  // 忽略目录，只处理文件
                    PathUrl pathUrl = new PathUrl();
                    String objectName = item.objectName();
                    String fullUrl = String.format("%s/%s/%s", modelConfiguration.getMinioEndpoint(), modelConfiguration.getBucketName(), objectName);
                    pathUrl.setUrl(fullUrl);
                    filePath.add(pathUrl);
                }
            }
            // 输出结果
            log.info("可访问的文件路径列表：" + filePath.toString());

        } catch (ErrorResponseException | InsufficientDataException | InternalException |
                 InvalidKeyException | InvalidResponseException | IOException |
                 NoSuchAlgorithmException | ServerException | XmlParserException e) {
            log.error("操作失败: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
        filePathResponse.setFilePath(filePath);
        return filePathResponse;
    }

    public String minioFolderPathSub(String minioPathFolder) {
        // 找到第一个和第二个下划线的位置
        Pattern pattern = Pattern.compile("([a-zA-Z0-9]+_[a-zA-Z0-9]+)$");
        Matcher matcher = pattern.matcher(minioPathFolder);
        String taskIdWithUuid = "";
        if (matcher.find()) {
            taskIdWithUuid = matcher.group(1);
        } else {
            System.out.println("未找到匹配内容");
        }
        return taskIdWithUuid;
    }

    public String modifyModelYaml(String objectPath, String dataSetPath) {
        // 需要将数据集目录 dataSetPath 转为容器内目录
        String minioPath = DataUtils.subStringUrl(dataSetPath);
        // 将minio路径转为容器内路径   容器路径+minio路径=容器内文件全路径
        //
        String ss = minioPath + dataSetPath;
        MinioClient minioClient = createMinioClient();

        try (
                InputStream originStream = minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(modelConfiguration.getBucketName())
                                .object(objectPath + "zhongchecovert/sifang_test/giao_zhongche.yaml")
                                .build())) {

            // 1. 增强YAML序列化配置
            YAMLFactory yamlFactory = new YAMLFactory()
                    .enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR)
                    .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)  // 确保字符串不添加引号
                    .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);

            ObjectMapper yamlMapper = new YAMLMapper(yamlFactory)
                    .setSerializationInclusion(JsonInclude.Include.NON_NULL);

            ObjectNode configTree = yamlMapper.readTree(originStream).deepCopy();

            // 2. 修改参数（保持原始格式）
            modifyConfigValue(configTree, "hydra.run.dir", "/Users/v_liuli04/Desktop/zhongche");
            modifyConfigValue(configTree, "model", "NewModelV2-001");

            // 3. 生成YAML内容（校验格式）
            String modifiedYaml = yamlMapper.writeValueAsString(configTree);
            validateYamlSyntax(modifiedYaml);  // 新增格式校验

            // 4. 修正流上传参数
            byte[] yamlBytes = modifiedYaml.getBytes(StandardCharsets.UTF_8);
            // 6. 回传更新
            try (ByteArrayInputStream newStream = new ByteArrayInputStream(yamlBytes)) {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(modelConfiguration.getBucketName())
                                .object("zhongchecovert/sifang_test/giao_zhongche.yaml")
                                .stream(newStream, newStream.available(), -1)
                                .build());
            }

            return "修改成功";
        } catch (Exception e) {
            log.error("配置文件修改失败", e);
            return "修改失败: " + e.getMessage();
        }
    }

    // 新增校验方法
    private void validateYamlSyntax(String yamlContent) throws IOException {
        try {
            new Yaml().load(yamlContent);
        } catch (Exception e) {
            throw new IOException("YAML语法校验失败: " + e.getMessage());
        }
    }


    // 增强版参数修改方法
    // 强化版参数修改方法
    private void modifyConfigValue(ObjectNode rootNode, String keyPath, Object newValue) {
        String[] pathSegments = keyPath.split("\\.");
        ObjectNode currentNode = rootNode;

        try {
            // 逐层遍历或创建路径
            for (int i = 0; i < pathSegments.length; i++) {
                String currentKey = pathSegments[i];
                JsonNode nextNode = currentNode.get(currentKey);

                // 处理叶子节点赋值
                if (i == pathSegments.length - 1) {
                    setNodeValue(currentNode, currentKey, newValue);
                    return;
                }

                // 处理中间节点
                if (nextNode == null || !nextNode.isObject()) {
                    // 自动创建中间映射节点
                    ObjectNode newChild = currentNode.putObject(currentKey);
                    currentNode = newChild;
                } else {
                    currentNode = (ObjectNode) nextNode;
                }
            }
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("路径[" + keyPath + "]存在类型冲突，无法修改");
        }
    }

    // 类型安全的值设置方法
    private void setNodeValue(ObjectNode parentNode, String key, Object value) {
        if (value instanceof String) {
            parentNode.put(key, (String) value);
        } else if (value instanceof Number) {
            parentNode.put(key, (Integer) value);
        } else if (value instanceof Boolean) {
            parentNode.put(key, (Boolean) value);
        } else {
            throw new IllegalArgumentException("不支持的数据类型: " + value.getClass());
        }
    }


    public void createPdParamsJson(MinioClient minioClient, Map<String, String> stringStringMap, String objectPath) {
        String density = stringStringMap.get("density");
        String referenceArea = stringStringMap.get("reference_area");
        String velocity = stringStringMap.get("velocity");
        // 2. 生成JSON内容
        PdParamsModel pdParamsModel = PdParamsModel
                .builder()
                .length(modelConfiguration.getLength())
                .width(modelConfiguration.getWidth())
                .height(modelConfiguration.getHeight())
                .clearance(modelConfiguration.getClearance())
                .slant(modelConfiguration.getSlant())
                .radius(modelConfiguration.getRadius())
                .velocity(velocity)
                .re(modelConfiguration.getRe())
                .referenceArea(referenceArea)
                .density(density)
                .computeNormal(modelConfiguration.isComputeNormal())
                .build();
        ObjectMapper mapper = new ObjectMapper();


        try {
            // 3. 转换为输入流
            ByteArrayInputStream inputStream = new ByteArrayInputStream(
                    mapper.writeValueAsString(pdParamsModel).getBytes("UTF-8")
            );
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(modelConfiguration.getBucketName())
                            .object(objectPath + "info.json")
                            .stream(inputStream, inputStream.available(), -1)
                            .build());

        } catch (InsufficientDataException | ServerException | ErrorResponseException | IOException |
                 NoSuchAlgorithmException | InvalidKeyException | InvalidResponseException | XmlParserException |
                 InternalException ex) {
            throw new RuntimeException(ex);
        }
    }


    public String queryReasonProcess(String modelTaskId, String objectName) {
        if (StringUtils.isEmpty(modelTaskId) || StringUtils.isEmpty(objectName)) {
            log.warn("输入参数无效: modelTaskId={}, objectName={}", modelTaskId, objectName);
            return "";
        }

        try {
            MinioClient minioClient = createMinioClient();
            String bucketName = validateBucketName();
            String folderPath = validateFolderPath(modelTaskId);

            String processPath = findObjectPath(minioClient, bucketName, folderPath, false, objectName);
            if (StringUtils.isEmpty(processPath)) {
                log.error("未查询到相关进度数据文件！modelTaskId: {}", modelTaskId);
                return "";
            }

            return readEpochFromObject(minioClient, bucketName, processPath);
        } catch (Exception e) {
            log.error("操作失败: {}, modelTaskId: {}", e.getMessage(), modelTaskId);
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 推理结果查询
     */
    public JSONArray queryReasonModel(String modelTaskId, String objectName) {
        try {
            MinioClient minioClient = createMinioClient();
            String bucketName = validateBucketName();
            String folderPath = validateFolderPath(modelTaskId);

            String processPath = findObjectPath(minioClient, bucketName, folderPath, false, objectName);
            if (StringUtils.isEmpty(processPath)) {
                log.error("未查询到相关模型数据文件！modelTaskId: {}", modelTaskId);
                return new JSONArray();
            }

            return readJsonFromObject(minioClient, bucketName, processPath);
        } catch (Exception e) {
            log.error("操作失败: {}, modelTaskId: {}", e.getMessage(), modelTaskId);
            e.printStackTrace();
        }
        return null;
    }

    public String queryReasonModelVtp(String modelTaskId, String objectName) {
        if (StringUtils.isEmpty(modelTaskId) || StringUtils.isEmpty(objectName)) {
            log.warn("输入参数无效: modelTaskId={}, objectName={}", modelTaskId, objectName);
            return "";
        }
        try {
            MinioClient minioClient = createMinioClient();
            String bucketName = validateBucketName();
            String folderPath = validateFolderPath(modelTaskId);

            return findObjectPath(minioClient, bucketName, folderPath, false, objectName);
        } catch (Exception e) {
            log.error("操作失败: {}, modelTaskId: {}", e.getMessage(), modelTaskId);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询推理日志
     *
     * @param modelTaskId 模型推理id
     * @param objectName  目标文件
     * @return 日志路径
     */
    public String queryReasonLog(String modelTaskId, String objectName) {
        if (StringUtils.isEmpty(modelTaskId) || StringUtils.isEmpty(objectName)) {
            log.warn("输入参数无效: modelTaskId={}, objectName={}", modelTaskId, objectName);
            return "";
        }
        try {
            MinioClient minioClient = createMinioClient();
            String bucketName = validateBucketName();
            String folderPath = validateFolderPath(modelTaskId);

            return findObjectPath(minioClient, bucketName, folderPath, false, objectName);
        } catch (Exception e) {
            log.error("操作失败: {}, modelTaskId: {}", e.getMessage(), modelTaskId);
            e.printStackTrace();
        }
        return null;
    }

    public MinioClient createMinioClient() {
        return MinioClient.builder()
                .endpoint(modelConfiguration.getMinioEndpoint())
                .credentials(modelConfiguration.getMinioAK(), modelConfiguration.getMinioSK())
                .build();
    }

    private String validateBucketName() throws Exception {
        String bucketName = modelConfiguration.getBucketName();
        if (StringUtils.isEmpty(bucketName)) {
            throw new Exception("存储桶名称无效！");
        }
        return bucketName;
    }

    /**
     * 验证文件夹路径
     *
     * @param modelTaskId 模型任务 ID
     * @return 文件夹路径
     */
    private String validateFolderPath(String modelTaskId) throws Exception {
        String folderPath = minioFolderPathSub(modelTaskId);
        if (StringUtils.isEmpty(folderPath)) {
            throw new Exception("文件夹路径无效！modelTaskId: " + modelTaskId);
        }
        return folderPath;
    }

    /**
     * 查找目标对象路径
     *
     * @param minioClient MinIO 客户端
     * @param bucketName  存储桶名称
     * @param folderPath  文件夹路径
     * @param objectName  目标文件名
     * @param recursive   是否递归
     * @return 对象路径
     */
    private String findObjectPath(MinioClient minioClient, String bucketName, String folderPath, boolean recursive, String objectName)
            throws Exception {
        try {
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!bucketExists) {
                throw new Exception("存储桶 '" + bucketName + "' 不存在！");
            }

            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(folderPath).recursive(recursive).build());

            for (Result<Item> result : results) {
                Item item = result.get();
                if (!item.isDir() && item.objectName().equals(objectName)) {
                    log.info("找到文件: {}", item.objectName());
                    return String.format("%s/%s/%s", modelConfiguration.getMinioEndpoint(), bucketName, item.objectName());
                }
            }
        } catch (Exception e) {
            throw new Exception("查找对象路径失败: " + e.getMessage(), e);
        }
        return "";
    }

    /**
     * 从对象中读取 epoch 值
     *
     * @param minioClient MinIO 客户端
     * @param bucketName  存储桶名称
     * @param objectPath  对象路径
     * @return epoch 值
     */
    private String readEpochFromObject(MinioClient minioClient, String bucketName, String objectPath) throws Exception {
        try {
            JSONArray jsonArray = readJsonFromObject(minioClient, bucketName, objectPath);
            if (jsonArray == null || jsonArray.isEmpty()) {
                throw new Exception("JSON 数组为空或格式不正确！");
            }
            JSONObject obj = jsonArray.getJSONObject(jsonArray.size() - 1);
            return String.valueOf(obj.get("epoch"));
        } catch (Exception e) {
            throw new Exception("读取 epoch 失败: " + e.getMessage(), e);
        }
    }

    /**
     * @param minioClient MinIO 客户端
     * @param bucketName  存储桶名称
     * @param objectPath  对象路径
     * @return JSONArray
     */
    private JSONArray readJsonFromObject(MinioClient minioClient, String bucketName, String objectPath) throws Exception {
        try (GetObjectResponse response = minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectPath)
                .build());
             BufferedReader reader = new BufferedReader(new InputStreamReader(response, StandardCharsets.UTF_8))) {

            StringBuilder contentBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                contentBuilder.append(line).append(System.lineSeparator());
            }

            String content = contentBuilder.toString();
            if (content.isEmpty()) {
                throw new Exception("文件内容为空！");
            }

            return JSONArray.parseArray(content);
        } catch (Exception e) {
            throw new Exception("读取 文件json数据 失败: " + e.getMessage(), e);
        }
    }

    public Map<String, String> parseMinioTextFile(String minioPath) {
        try {
            // 解析Minio路径
            URI uri = new URI(minioPath);
//            String endpoint = uri.getScheme() + "://" + uri.getAuthority();
            String path = uri.getPath();

            // 提取bucket名称和前缀
            String[] pathSegments = path.split("/");
            List<String> nonEmptySegments = new ArrayList<>();
            for (String segment : pathSegments) {
                if (!segment.isEmpty()) {
                    nonEmptySegments.add(segment);
                }
            }
            if (nonEmptySegments.isEmpty()) {
                throw new IllegalArgumentException("Invalid Minio path");
            }

            String bucketName = nonEmptySegments.get(0);
            StringBuilder prefixBuilder = new StringBuilder();
            for (int i = 1; i < nonEmptySegments.size(); i++) {
                prefixBuilder.append(nonEmptySegments.get(i)).append("/");
            }
            String prefix = prefixBuilder.toString();

            // 创建Minio客户端（需要替换为实际凭证）
            MinioClient minioClient = createMinioClient();

            // 查找txt文件
            String txtObjectName = null;
            ListObjectsArgs listArgs = ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .prefix(prefix)
                    .recursive(false)
                    .build();

            for (Result<Item> result : minioClient.listObjects(listArgs)) {
                Item item = result.get();
                if (item.objectName().endsWith(".txt")) {
                    txtObjectName = item.objectName();
                    break;
                }
            }
            if (txtObjectName == null) {
                log.error("No .txt file found in specified path");
            }

            // 读取并解析文件内容
            Map<String, String> config = new HashMap<>();
            try (InputStream stream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(txtObjectName)
                            .build());
                 BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {

                String line;
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
//                    if (line.isEmpty()) continue;

                    String[] parts = line.split("\\s+", 2);
                    if (parts.length == 2) {
                        config.put(parts[0], parts[1]);
                    }
                }
            }
            return config;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new HashMap<>();
    }

    public String preJob(String dataSetPath, String modeTaskId) {
        String folder = DataUtils.minioFolderPathSub(modeTaskId);
        assert modelConfiguration != null;
        String objectPath = modelConfiguration.getMinioSiFangFolderPrefix() + folder + "/";
        Map<String, String> stringStringMap = parseMinioTextFile(dataSetPath);

        MinioClient minioClient = createMinioClient();
        // 写入预处理模型所需要的json文件
        createPdParamsJson(minioClient, stringStringMap, objectPath);

        // 修改yaml文件，例如预处理路径
//        modifyModelYaml();

        // 调用国兴预处理接口
        return "";
    }


}
